#define UNITY_Yooasset
#if UNITY_Yooasset
using Panty.QF;
using System;
using System.IO;
using UnityEngine;
using YooAsset;
using static UnityEngine.Rendering.HDROutputUtils;

namespace Panty.Test
{
    public interface IYooassetDownLoadModule : IModule
    {

        EPlayMode PlayMode { get; set; }
        string defaultHostServer { get; set; }
        string fallbackHostServer { get; set; }
        string appVersion { set; get; }
        void GotoDownload();
        (int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes) GetDowonLoadinfo();
    }
    public class YooassetDownLoadModule : AbsModule, IYooassetDownLoadModule
    {

        #region 字段
        private int totalDownloadCount { get; set; }
        private int currentDownloadCount { get; set; }
        private long totalDownloadBytes { get; set; }
        private long currentDownloadBytes { get; set; }

        public EPlayMode PlayMode { get; set; }

        public string defaultHostServer { get; set; }

        public string fallbackHostServer { get; set; }

        public string appVersion { get; set; }
        #endregion
        public override bool Preload => true;
        protected override void OnInit()
        {
        }

        #region Yooasset初始化与资源下载验证
        async void IYooassetDownLoadModule.GotoDownload()
        {

            // 创建资源包裹类
            var package = YooAssets.TryGetPackage("DefaultPackage");
            if (package == null)
                package = YooAssets.CreatePackage("DefaultPackage");
            YooAssets.SetDefaultPackage(package);

            InitializationOperation initializationOperation = null;
            var buildPipeline = EDefaultBuildPipeline.BuiltinBuildPipeline;
            var packageName = "DefaultPackage";
            
            // 编辑器下的模拟模式
            if (PlayMode == EPlayMode.EditorSimulateMode)
            {
                var simulateBuildResult = EditorSimulateModeHelper.SimulateBuild(buildPipeline, packageName);
                var createParameters = new EditorSimulateModeParameters();
                createParameters.EditorFileSystemParameters = FileSystemParameters.CreateDefaultEditorFileSystemParameters(simulateBuildResult);
                initializationOperation = package.InitializeAsync(createParameters);
            }

            // 单机运行模式
            if (PlayMode == EPlayMode.OfflinePlayMode)
            {
                var createParameters = new OfflinePlayModeParameters();
                createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(new FileOffsetDecryption());
                initializationOperation = package.InitializeAsync(createParameters);
            }

            // 联机运行模式
            if (PlayMode == EPlayMode.HostPlayMode)
            {
                string defaultHostServer = GetHostServerURL();
                string fallbackHostServer = GetHostServerURL();
                IRemoteServices remoteServices = new RemoteServices(defaultHostServer, fallbackHostServer);
                var createParameters = new HostPlayModeParameters();
                createParameters.BuildinFileSystemParameters = FileSystemParameters.CreateDefaultBuildinFileSystemParameters(new FileOffsetDecryption());
                createParameters.CacheFileSystemParameters = FileSystemParameters.CreateDefaultCacheFileSystemParameters(remoteServices);
                initializationOperation = package.InitializeAsync(createParameters);
            }
            // WebGL运行模式
            if (PlayMode == EPlayMode.WebPlayMode)
            {
                var createParameters = new WebPlayModeParameters();
                createParameters.WebFileSystemParameters = FileSystemParameters.CreateDefaultWebFileSystemParameters();
                initializationOperation = package.InitializeAsync(createParameters);
            }

           await initializationOperation.Task;

            // 如果初始化失败弹出提示界面
            if (initializationOperation.Status != EOperationStatus.Succeed)
            {
                Debug.LogWarning($"{initializationOperation.Error}");               
            }
            else
            {
                Debug.Log("远程资源包初始化成功");
            }

            //更新获取对比远程文件版本
            var operation = package.RequestPackageVersionAsync();
            await operation.Task;
            if (operation.Status == EOperationStatus.Succeed)
            {
                //获取成功
                string packageVersion = operation.PackageVersion;
                Debug.Log($"Request package Version : {packageVersion}，开始进入资源更新.....");
                var operationszy = package.UpdatePackageManifestAsync(packageVersion);
                await operationszy.Task;
                if (operationszy.Status == EOperationStatus.Succeed)
                {
                    //更新成功
                    Download();

                }
                else
                {
                    //更新失败
                    Debug.LogError(operationszy.Error);
                }
            }
            else
            {
                //版本获取更新更新失败
                Debug.LogError(operation.Error);
            }
        }

        async void Download()
        {
            int downloadingMaxNum = 10;
            int failedTryAgain = 3;
            var package = YooAssets.GetPackage("DefaultPackage");
            var downloader = package.CreateResourceDownloader(downloadingMaxNum, failedTryAgain);

            //没有需要下载的资源
            if (downloader.TotalDownloadCount == 0)
            {
                //直接进入游戏
                Debug.Log("没有需要下载的资源进入游戏成功");
                this.EnumEvent<E_Gameevent>(E_Gameevent.进入游戏);
                return;
            }
            //需要下载的文件总数和总大小
            int totalDownloadCount = downloader.TotalDownloadCount;
            long totalDownloadBytes = downloader.TotalDownloadBytes;
            Debug.Log($"下载资源总数:{totalDownloadCount} 大小:{totalDownloadBytes}");
            //注册回调方法
            downloader.OnDownloadErrorCallback = OnDownloadErrorFunction;
            downloader.OnDownloadProgressCallback = OnDownloadProgressUpdateFunction;
            downloader.OnDownloadOverCallback = OnDownloadOverFunction;
            downloader.OnStartDownloadFileCallback = OnStartDownloadFileFunction;

            //开启下载
            downloader.BeginDownload();

            //开启加载过渡动画
            await downloader.Task;

            //检测下载结果
            if (downloader.Status == EOperationStatus.Succeed)
            {
                //下载成功
                Debug.Log("下载成功进入游戏成功");
                this.EnumEvent<E_UICanvasEvt>(E_UICanvasEvt.过渡动画结束);
                this.EnumEvent<E_Gameevent>(E_Gameevent.进入游戏);
            }
            else
            {
                //下载失败
                Debug.LogError("下载资源失败");
            }
        }
        #endregion

        #region yooasset下载回调函数
        /// <summary>
        /// 下载数据大小
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="sizeBytes"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnStartDownloadFileFunction(string fileName, long sizeBytes)
        {
            Debug.Log(string.Format("开始下载：文件名：{0}, 文件大小：{1}", fileName, sizeBytes));
        }
        /// <summary>
        /// 下载完成与否
        /// </summary>
        /// <param name="isSucceed"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnDownloadOverFunction(bool isSucceed)
        {
            Debug.Log("下载" + (isSucceed ? "成功" : "失败"));
        }

        /// <summary>
        /// 更新中
        /// </summary>
        /// <param name="totalDownloadCount"></param>
        /// <param name="currentDownloadCount"></param>
        /// <param name="totalDownloadBytes"></param>
        /// <param name="currentDownloadBytes"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnDownloadProgressUpdateFunction(int totalDownloadCounts, int currentDownloadCounts, long totalDownloadBytess, long currentDownloadBytess)
        {
            totalDownloadCount = totalDownloadCounts;
            currentDownloadCount = currentDownloadCounts;
            totalDownloadBytes = totalDownloadBytess;
            currentDownloadBytes = currentDownloadBytess;
            Debug.Log(string.Format("文件总数：{0}, 已下载文件数：{1}, 下载总大小：{2}, 已下载大小：{3}", totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes));
            this.EnumEvent<E_UICanvasEvt, (int tall, int curretn, long tallbytes, 
                long currentbytes, bool isplayvideo, bool isassetupdata)>(E_UICanvasEvt.过渡动画开始,
                (
                totalDownloadCounts,
                currentDownloadCounts,
                totalDownloadBytess,
                currentDownloadBytess,
                false,
                true
                ));

        }
        /// <summary>
        /// 下载出错
        /// </summary>
        /// <param name="fileName"></param>
        /// <param name="error"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void OnDownloadErrorFunction(string fileName, string error)
        {
            Debug.LogError(string.Format("下载出错：文件名：{0}, 错误信息：{1}", fileName, error));
        }

        /// <summary>
        /// 获取下载进度
        /// </summary>
        /// <returns></returns>
        (int totalDownloadCount, int currentDownloadCount, long totalDownloadBytes, long currentDownloadBytes) IYooassetDownLoadModule.GetDowonLoadinfo()
        {
            return (totalDownloadCount, currentDownloadCount, totalDownloadBytes, currentDownloadBytes);
        }
        #endregion

        #region yooasset远端资源地址查询服务类
        /// <summary>
        /// 获取资源服务器地址
        /// </summary>
        private string GetHostServerURL()
        {
            //string hostServerIP = "http://10.0.2.2"; //安卓模拟器地址
            string hostServerIP = defaultHostServer;
            string appVersions = appVersion;// "v1.0";

#if UNITY_EDITOR
            if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.Android)
                return $"{hostServerIP}/CDN/Android/{appVersions}";
            else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.iOS)
                return $"{hostServerIP}/CDN/IPhone/{appVersions}";
            else if (UnityEditor.EditorUserBuildSettings.activeBuildTarget == UnityEditor.BuildTarget.WebGL)
                return $"{hostServerIP}/CDN/WebGL/{appVersions}";
            else
                return $"{hostServerIP}/CDN/PC/{appVersions}";
#else
        if (Application.platform == RuntimePlatform.Android)
            return $"{hostServerIP}/CDN/Android/{appVersions}";
        else if (Application.platform == RuntimePlatform.IPhonePlayer)
            return $"{hostServerIP}/CDN/IPhone/{appVersions}";
        else if (Application.platform == RuntimePlatform.WebGLPlayer)
            return $"{hostServerIP}/CDN/WebGL/{appVersions}";
        else
            return $"{hostServerIP}/CDN/PC/{appVersions}";
#endif
        }

        /// <summary>
        /// 远端资源地址查询服务类
        /// </summary>
        private class RemoteServices : IRemoteServices
        {
            private readonly string _defaultHostServer;
            private readonly string _fallbackHostServer;

            public RemoteServices(string defaultHostServer, string fallbackHostServer)
            {
                _defaultHostServer = defaultHostServer;
                _fallbackHostServer = fallbackHostServer;
            }
            string IRemoteServices.GetRemoteMainURL(string fileName)
            {
                return $"{_defaultHostServer}/{fileName}";
            }
            string IRemoteServices.GetRemoteFallbackURL(string fileName)
            {
                return $"{_fallbackHostServer}/{fileName}";
            }
        }

        /// <summary>
        /// 资源文件流加载解密类
        /// </summary>
        private class FileStreamDecryption : IDecryptionServices
        {
            /// <summary>
            /// 同步方式获取解密的资源包对象
            /// 注意：加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
                managedStream = bundleStream;
                return AssetBundle.LoadFromStream(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());
            }

            /// <summary>
            /// 异步方式获取解密的资源包对象
            /// 注意：加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                BundleStream bundleStream = new BundleStream(fileInfo.FileLoadPath, FileMode.Open, FileAccess.Read, FileShare.Read);
                managedStream = bundleStream;
                return AssetBundle.LoadFromStreamAsync(bundleStream, fileInfo.FileLoadCRC, GetManagedReadBufferSize());
            }

            /// <summary>
            /// 获取解密的字节数据
            /// </summary>
            byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo)
            {
                throw new System.NotImplementedException();
            }

            /// <summary>
            /// 获取解密的文本数据
            /// </summary>
            string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo)
            {
                throw new System.NotImplementedException();
            }

            private static uint GetManagedReadBufferSize()
            {
                return 1024;
            }
        }

        /// <summary>
        /// 资源文件偏移加载解密类
        /// </summary>
        private class FileOffsetDecryption : IDecryptionServices
        {
            /// <summary>
            /// 同步方式获取解密的资源包对象
            /// 注意：加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundle IDecryptionServices.LoadAssetBundle(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                managedStream = null;
                return AssetBundle.LoadFromFile(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());
            }

            /// <summary>
            /// 异步方式获取解密的资源包对象
            /// 注意：加载流对象在资源包对象释放的时候会自动释放
            /// </summary>
            AssetBundleCreateRequest IDecryptionServices.LoadAssetBundleAsync(DecryptFileInfo fileInfo, out Stream managedStream)
            {
                managedStream = null;
                return AssetBundle.LoadFromFileAsync(fileInfo.FileLoadPath, fileInfo.FileLoadCRC, GetFileOffset());
            }

            /// <summary>
            /// 获取解密的字节数据
            /// </summary>
            byte[] IDecryptionServices.ReadFileData(DecryptFileInfo fileInfo)
            {
                throw new System.NotImplementedException();
            }

            /// <summary>
            /// 获取解密的文本数据
            /// </summary>
            string IDecryptionServices.ReadFileText(DecryptFileInfo fileInfo)
            {
                throw new System.NotImplementedException();
            }

            private static ulong GetFileOffset()
            {
                return 32;
            }
        }
    }

    /// <summary>
    /// 资源文件解密流
    /// </summary>
    public class BundleStream : FileStream
    {
        public const byte KEY = 64;

        public BundleStream(string path, FileMode mode, FileAccess access, FileShare share) : base(path, mode, access, share)
        {
        }
        public BundleStream(string path, FileMode mode) : base(path, mode)
        {
        }

        public override int Read(byte[] array, int offset, int count)
        {
            var index = base.Read(array, offset, count);
            for (int i = 0; i < array.Length; i++)
            {
                array[i] ^= KEY;
            }
            return index;
        }
    }
    #endregion

}


#endif